﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.IO;
using MebAppMarketplace.Helpers;
using Newtonsoft.Json;
using MebAppMarketplace.JsonModel;
using MebAppMarketplace.Model;
using System.Threading.Tasks;
using System.IO.Compression;

namespace MebAppMarketplace.Biz
{
    public class AppService : BaseService<Model.App>
    {
        private readonly UserService userService;
        public AppService(UserService userService) : base(userService.Db)
        {
            this.userService = userService;
        }

        public List<AppJson> GetApps(string kw, int? category, int page)
        {
            return DbSet
                .Contain(x => x.Name + x.Description, kw)
                .Contain(x => x.CategoryPath, category)
                .Select(x => new AppJson()
                {
                    ID = x.ID,
                    Name = x.Name,
                    Manufacturer = x.Manufacturer,
                    NDownload = x.NDownload,
                    OLTime = x.OLTime,
                    Price = x.Price,
                    Version = x.Version,
                    Icon = x.Icon,
                    HomeUrl = x.Publisher,
                    Description = x.Description,
                    IsOfficalApp = x.IsOfficalApp
                })
                .GetList(page);
        }

        public AppBase GetAppBase(Guid id)
        {
            return Db.Set<AppBase>().Find(id);
        }

        public List<AppVersion> GetVersionList(Guid id, int page)
        {
            return Db.Set<AppVersion>().Where(x => x.AppID == id).OrderBy(x => x.OLTime).GetList(page);
        }
        //TODO: SEND EMAIL
        public void Reject(Guid id, string reason)
        {
            var appBase = Db.Set<AppBase>().Find(id);
            if (appBase.AppJson == null)
            {
                return;
            }
            appBase.AppJson = null;
            Db.Entry(appBase).State = EntityState.Modified;
            Db.SaveChanges();
        }
        //TODO: SEND EMAIL
        public void Approve(Guid id)
        {
            var appBase = Db.Set<AppBase>().Find(id);
            if (appBase.AppJson == null)
            {
                return;
            }
            else
            {
                var info = JsonConvert.DeserializeObject<AppInfo>(appBase.AppJson);
                appBase.AppJson = null;
                string path = Path.Combine(AppContext.BaseDirectory, "../apps/" + info.AppID);
                string srcLocation = Path.Combine(path, "newapp.zip");
                string dstLocation = Path.Combine(path, "app.zip");
                string appJson = Path.Combine(path, "app.json");
                var app = DbSet.Find(info.AppID);
                if (app == null)
                {
                    app = new Model.App();
                    app.ID = appBase.ID;
                    app.OwnerID = appBase.OwnerID;
                    app.Location = "../apps/" + info.AppID;
                    app.Name = info.Name;
                    app.Publisher = info.Publisher;
                    app.Icon = info.Icon;
                    app.OLTime = DateTime.Now;
                    app.Description = info.Description;
                    app.Manufacturer = info.Manufacturer;
                    app.Price = info.Price;
                    app.Version = 1;
                    app.Enabled = true;
                    DbSet.Add(app);


                }
                else
                {
                    //保存旧版数据
                    var version = app.Version;
                    var item = new Model.AppVersion();
                    item.ID = Guid.NewGuid();
                    item.AppID = app.ID;
                    item.NDownload = app.NDownload;
                    item.Description = app.Description;
                    item.Icon = app.Icon;
                    item.Location = app.Location + "/" + app.Version;
                    item.OLTime = app.OLTime;
                    item.Name = app.Name;
                    item.Manufacturer = app.Manufacturer;
                    item.Description = app.Description;
                    item.Version = app.Version;
                    Db.Set<Model.AppVersion>().Add(item);

                    //更新为新版数据
                    app.Name = info.Name;
                    app.Location = "../apps/" + info.AppID;
                    app.Publisher = info.Publisher;
                    app.Icon = info.Icon;
                    app.Description = info.Description;
                    app.Manufacturer = info.Manufacturer;
                    app.Price = info.Price;
                    app.OLTime = DateTime.Now;
                    app.Version += 1;
                    app.Enabled = true;
                    Db.Entry(app).State = EntityState.Modified;

                    //备份将要被更新的app
                    string dstBackupLocation = Path.Combine(path, version.ToString());
                    if (!Directory.Exists(dstBackupLocation))
                    {
                        Directory.CreateDirectory(dstBackupLocation);
                    }
                    if (File.Exists(dstLocation))
                    {
                        var dstBackupFile = Path.Combine(dstBackupLocation, "app.zip");
                        if (File.Exists(dstBackupFile))
                            File.Delete(dstBackupFile);
                        File.Move(dstLocation, dstBackupFile);
                    }
                }
                //更新App文件
                if (!Directory.Exists(path))
                {
                    Directory.CreateDirectory(path);
                }
                if (File.Exists(dstLocation))
                    File.Delete(dstLocation);

                if (File.Exists(appJson))
                {
                    File.Delete(appJson);
                }
                File.Move(srcLocation, dstLocation);

                //保存当前版本
                appBase.Version = app.Version;
            }
            Db.SaveChanges();
        }

        /// <summary>
        /// 检查是否有新版本
        /// </summary>
        /// <param name="id"></param>
        /// <param name="version"></param>
        /// <returns>Null表示没有新版本</returns>
        public AppJson GetNewVersion(Guid id, int version)
        {
            var m = Db.Set<AppBase>().Find(id);
            if (m.Deleted || m.IsOffShelve)
                return null;
            if (m.Version <= version)
                return null;
            var x = DbSet.Find(id);
            return new AppJson()
            {
                ID = x.ID,
                Name = x.Name,
                Manufacturer = x.Manufacturer,
                NDownload = x.NDownload,
                OLTime = x.OLTime,
                Price = x.Price,
                Version = x.Version,
                Icon = x.Icon,
                HomeUrl = x.Publisher,
                Description = x.Description,
                IsOfficalApp = x.IsOfficalApp
            };
        }

        public async Task<UserApp> GetUserApp(Guid id)
        {
            var uid = await userService.GetLoginUserIDAsync();
            return Db.Set<UserApp>().FirstOrDefault(x => x.AppID == id && x.UserID == uid);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="version"></param>
        /// <param name="file"></param>
        /// <param name="hash"></param>
        /// <returns>404 NotFound; 304 NotModified</returns>
        public string GetAppFile(Guid id, int version, string file, string hash)
        {
            var app = DbSet.FirstOrDefault(x => x.ID == id && x.Version == version && x.Enabled);
            if (app != null && !string.IsNullOrEmpty(app.Location))
            {
                //return app.Location + "/app.zip";
                var zipFile = System.IO.Path.Combine(AppContext.BaseDirectory, app.Location, "app.zip");
                if (!File.Exists(zipFile))
                {
                    return "404";
                }
                using (var zipArchive = new ZipArchive(File.Open(zipFile, FileMode.Open, FileAccess.Read, FileShare.Read), ZipArchiveMode.Read))
                {
                    foreach (var item in zipArchive.Entries)
                    {
                        if (Path.GetFileName(item.Name).Equals(file, StringComparison.OrdinalIgnoreCase))
                        {
                            var temp = Path.GetTempFileName();
                            using (Stream stream = item.Open(), fs = File.Open(temp, FileMode.Open))
                            {
                                var versionFileHash = HashHelper.ComputeStreamMD5(fs);
                                if (versionFileHash == hash)
                                {
                                    return "304";
                                }
                                else
                                {
                                    stream.CopyTo(fs);
                                }

                            }
                            return temp;
                        }
                    }
                }
            }
            return "404";
        }

        public List<AppJson> GetAppUpdates(List<CheckUpdateItem> checkList)
        {
            var idList = new List<AppJson>();
            var checkIdList = checkList.Select(x => x.ID);
            var apps = DbSet.Where(x => checkIdList.Contains(x.ID)).ToList().ToDictionary(x => x.ID);
            foreach (var item in checkList)
            {
                if (apps.ContainsKey(item.ID))
                {
                    if (apps[item.ID].Version > item.Version)
                    {
                        var x = apps[item.ID];
                        idList.Add(new AppJson()
                        {
                            ID = x.ID,
                            Name = x.Name,
                            Manufacturer = x.Manufacturer,
                            NDownload = x.NDownload,
                            OLTime = x.OLTime,
                            Price = x.Price,
                            Version = x.Version,
                            Icon = x.Icon,
                            HomeUrl = x.Publisher,
                            Description = x.Description,
                            IsOfficalApp = x.IsOfficalApp
                        });
                    }
                }
            }
            return idList;
        }

        /// <summary>
        /// 用户安装app后执行该方法
        /// </summary>
        /// <returns></returns>
        public async Task AddAppInstall(Guid id)
        {
            var userId = await userService.GetLoginUserIDAsync();
            var userApp = Db.Set<UserApp>().FirstOrDefault(x => x.AppID == id && x.UserID == userId);
            if (userApp == null)
            {
                Db.Set<UserApp>().Add(new UserApp { ID = Guid.NewGuid(), AppID = id, UserID = userId, CreateTime = DateTime.Now });
            }
            else if (userApp.IsDelete)
            {
                userApp.IsDelete = false;
                Db.Entry(userApp).State = EntityState.Modified;
            }
            Db.SaveChanges();
        }

        /// <summary>
        /// 用户卸载app后执行该方法
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task RemoveAppInstall(Guid id)
        {
            var userId = await userService.GetLoginUserIDAsync();
            var userApp = Db.Set<UserApp>().FirstOrDefault(x => x.AppID == id && x.UserID == userId);
            if (userApp != null)
            {
                userApp.IsDelete = true;
                Db.Entry(userApp).State = EntityState.Modified;
                Db.SaveChanges();
            }
        }

        /// <summary>
        /// App运行次数统计
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task IncreaseNAppRun(Guid id, int version)
        {
            var userId = await userService.GetLoginUserIDAsync();
            var userApp = Db.Set<UserApp>().First(x => x.AppID == id && x.UserID == userId);
            if (userApp.FirstRunTime == null)
            {
                userApp.FirstRunTime = DateTime.Now;
            }
            userApp.LastRunTime = DateTime.Now;
            userApp.NAppRuns++;
            Db.Entry(userApp).State = EntityState.Modified;
            Db.SaveChanges();
        }
        /// <summary>
        /// App核心功能使用统计
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task IncreaseNCorRun(Guid id, int version)
        {
            var userId = await userService.GetLoginUserIDAsync();
            var userApp = Db.Set<UserApp>().First(x => x.AppID == id && x.UserID == userId);
            userApp.NCorRuns++;
            Db.Entry(userApp).State = EntityState.Modified;
            Db.SaveChanges();
        }
        //
        public void IncreaseNDownload(AppState loc, Guid id, int version)
        {
            var m = DbSet.Find(id);
            if (m != null && m.Version == version)
            {
                m.NDownload++;
            }
            Db.Entry(m).State = EntityState.Modified;
            Db.SaveChanges();
        }

        /// <summary>
        /// 获取审核列表
        /// </summary>
        /// <param name="page"></param>
        /// <returns></returns>
        public List<AppBase> GetAuditList(string kw, int page)
        {
            return Db.Set<AppBase>()
                .Equal(x => x.Name + x.Description + x.AppJson, kw)
                .GetList(page);
        }

        public AppState GetAppState(Guid id, int version /*停用该参数，因为版本更新时客户端未刷新会造成客户端找不到app指定版本而下载失败*/)
        {
            var appbase = Db.Set<AppBase>().Find(id);
            if (appbase == null)
                return AppState.AppNoFound;
            if (appbase.IsOffShelve)
                return AppState.AppOffShelve;
            var app = DbSet.Find(id);
            if (!app.Enabled)
            {
                return AppState.AppUnavailable;
            }
            return AppState.Available;
        }

        public bool IsAppExists(Guid id, int version)
        {
            var appbase = Db.Set<AppBase>().Find(id);
            if (appbase == null)
                return false;
            var app = DbSet.Find(id);
            if (app.Version == version)
            {
                if (!app.Enabled)
                    return false;
                return true;
            }
            else
            {
                return Db.Set<AppVersion>().Any(x => x.AppID == id && x.Version == version && x.Enabled);
            }
        }

        public string GetAppJson(Guid id, int version)
        {
            var app = DbSet.FirstOrDefault(x => x.ID == id && x.Version == version && x.Enabled);
            if (app != null && !string.IsNullOrEmpty(app.Location))
            {
                //return app.Location + "/app.zip";
                var file = System.IO.Path.Combine(AppContext.BaseDirectory, app.Location, "app.zip");
                if (File.Exists(file))
                {
                    //该文件缓存应该在下个版本审核通过时删除
                    var appJson = System.IO.Path.Combine(AppContext.BaseDirectory, app.Location, "app.json");
                    if (File.Exists(appJson))
                        return appJson;
                    else
                    {
                        using (var archive = ZipFile.Open(file, ZipArchiveMode.Read))
                        {
                            foreach (var entry in archive.Entries)
                            {
                                if (entry.Name.Equals("app.json", StringComparison.OrdinalIgnoreCase))
                                {
                                    using (var stream = entry.Open())
                                    {
                                        using (var fs = File.Create(appJson))
                                        {
                                            stream.CopyTo(fs);
                                        }
                                        return appJson;
                                    }
                                }
                            }
                        }
                    }
                }
                File.WriteAllText("api.app.getupdatexml.log", file);
            }
            File.WriteAllText("api.app.getupdatexml.log", "APP_NOT_FOUND|EMPTY_APP_LOCATION");
            return null;
        }

        public string GetZipFromAppList(Guid id, int version)
        {
            var app = DbSet.FirstOrDefault(x => x.ID == id && x.Version == version && x.Enabled);
            if (app != null && !string.IsNullOrEmpty(app.Location))
            {
                //return app.Location + "/app.zip";
                return System.IO.Path.Combine(AppContext.BaseDirectory, app.Location, "app.zip");
            }
            return null;
        }

        public bool IsAppNameExists(string name)
        {
            name = name.Trim().ToLower();
            var svc = AsService<AppBase>();
            return svc.DbSet.Any(x => x.Name == name);
        }

        public async Task CreateApp(string name, string description)
        {
            var appBase = new AppBase();
            appBase.OwnerID = await userService.GetLoginUserIDAsync();
            appBase.ID = Guid.NewGuid();
            appBase.Name = name;
            appBase.IsOffShelve = false;//默认在架销售状态
            appBase.Description = description;
            appBase.CreateTime = DateTime.Now;
            var svc = AsService<AppBase>();
            svc.DbSet.Add(appBase);
            svc.Db.SaveChanges();
        }

        public List<AppJson> GetOfficialApps(int? category, int page)
        {
            var str = $"[{category}]";
            return DbSet
                .Where(x => x.IsOfficalApp)
                .Contain(x => x.CategoryPath, str)
                .Select(x => new AppJson
                {
                    ID = x.ID,
                    Name = x.Name,
                    Manufacturer = x.Manufacturer,
                    NDownload = x.NDownload,
                    OLTime = x.OLTime,
                    Price = x.Price,
                    Version = x.Version,
                    Icon = x.Icon,
                    HomeUrl = x.Publisher,
                    Description = x.Description,
                    IsOfficalApp = x.IsOfficalApp
                })
                .GetList(page);
        }

        /// <summary>
        /// 获取首页推荐的
        /// </summary>
        /// <returns></returns>
        public IQueryable<Model.App> HomeRecommended()
        {
            return DbSet.Where(x => x.HomeRecommend);
        }

        public List<AppBase> GetDeveloperApps(Guid id, string kw, int page)
        {
            return this.AsService<Model.AppBase>()
                .DbSet.Where(x => x.OwnerID == id)
                .Contain(x => x.Name + x.Description, kw)
                .OrderByDescending(x => x.CreateTime).GetList(page);
        }

        public async Task ExtractAppZip(string tempFile)
        {
            string unzipPath = tempFile + ".files\\";
            string appJson = Path.Combine(unzipPath, "app.json");
            using (var fs = File.Open(tempFile, FileMode.Open))
            {
                //解压到临时目录, 检查app.json是否存在
                ZipHelper.UnZip(fs, unzipPath);
                if (!File.Exists(appJson))
                {
                    throw new Exception("应用包必须包含app.json文件");
                }
            }
            AppInfo info = null;
            try
            {
                info = JsonConvert.DeserializeObject<AppInfo>(File.ReadAllText(appJson));
            }
            catch (Exception ex)
            {
                throw new Exception("无法解析的app.json文件", ex);
            }
            var appId = info.AppID;

            var appBaseService = this.AsService<Model.AppBase>();
            var appBase = appBaseService.DbSet.Find(appId);
            if (appBase == null)
            {
                throw new Exception("无效的AppID, 错误代码0x40000001");
            }
            var userId = await userService.GetLoginUserIDAsync();
            if (userId != appBase.OwnerID)
            {
                throw new Exception("无效的AppID, 错误代码0x40000002");
            }
            string path = Path.Combine(AppContext.BaseDirectory, "../apps/" + appId);
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            //保存待审核文件到newapp.zip
            var dstLocation = Path.Combine(path, "newapp.zip");
            if (File.Exists(dstLocation))
            {
                File.Delete(dstLocation);
            }
            File.Move(tempFile, dstLocation);

            //保存待审核信息, 如果之前有值将会覆盖该值。
            appBase.AppJson = File.ReadAllText(appJson);
            Db.Entry(appBase).State = EntityState.Modified;
            Db.SaveChanges();
        }

        /// <summary>
        /// 获取指定栏目推荐的
        /// </summary>
        /// <param name="classify"></param>
        /// <returns></returns>
        public IQueryable<Model.App> CatetoryRecommend(int category)
        {
            var str = $"[{category}]";
            return DbSet.Where(x => x.CategoryRecommend).Contain(x => x.CategoryPath, str);
        }
    }
}
